package org.njo.webapp.templates.config;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;

import javax.servlet.ServletContext;
import javax.servlet.UnavailableException;

import org.apache.commons.digester.Digester;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * 
 * @author yu.peng
 */
public class ConfigLoadHelper {

    // TODO:完成logger

    public final static String TEMPLATES_CONFIG_APPLICATION_KEY = "org.njo.webapp.templates.config.ConfigLoadHelper.APPLICATION_KEY";
    
    private final static String DEFAULT_CONFIG_FILE = "/WEB-INF/templates-config.xml";
    protected String registrations[] = {
            "-//Njo Software Foundation//DTD Templates Configuration 1.0//EN",
            "/org/njo/webapp/templates/resources/templates-config.dtd"
    };

    private ConfigLoadHelper() {

    }

    private static ConfigLoadHelper helper = null;;
    public static ConfigLoadHelper getInstance() {
        if (helper != null) {
            return helper;
        }
        helper = new ConfigLoadHelper();
        return helper;
    }
    
    
    private final static String PATTERNS_CONFIG_KEY = "p";
    private final static String FORMATERS_CONFIG_KEY = "f";
    
    
    /**
     * 根据指定的patternid,取得相应pattern配置对象.
     * 
     * @param context
     * @param patternId
     * @return
     */
    public PatternConfig getPatternConfig(ServletContext context, String patternId) {
        Map<String, Map<String, Object>> templatesMapping = (Map) context.getAttribute(TEMPLATES_CONFIG_APPLICATION_KEY);
        Map<String, PatternConfig> pConfigs = (Map)templatesMapping.get(PATTERNS_CONFIG_KEY);
        return pConfigs.get(patternId);
    }
    
    /**
     * 根据指定的formatterid取得相应formatter配置对象.
     * 
     * @param context
     * @param formatterId
     * @return
     */
    public FormatterConfig getFormatterConfig(ServletContext context, String formatterId) {
        Map<String, Map<String, Object>> templatesMapping = (Map) context.getAttribute(TEMPLATES_CONFIG_APPLICATION_KEY);
        Map<String, FormatterConfig> fConfigs = (Map)templatesMapping.get(FORMATERS_CONFIG_KEY);
        return fConfigs.get(formatterId);
    }
    
    
    /**
     * 读取配置文件,并将其保存在ServletContext中.
     * 
     * 如果ServletContext中已经存在配置信息,那么就不会再次读取.
     * 
     * @param context
     * @param paths
     */
    public void loadConfig(ServletContext context, String config_file_paths) throws UnavailableException {
        Map templatesMapping = (Map) context
                .getAttribute(TEMPLATES_CONFIG_APPLICATION_KEY);

        if (templatesMapping != null) {
            return;
        }
        
        
        Digester digester = createConfigDigester();
        Map<String, PatternConfig> patternMapping = new LinkedHashMap<String, PatternConfig>();
        Map<String, FormatterConfig> formatterMapping = new LinkedHashMap<String, FormatterConfig>();

        try {
            // Process each specified resource path
            while (config_file_paths.length() > 0) {
                String config_file_path = null;
                int comma = config_file_paths.indexOf(',');
                if (comma >= 0) {
                    config_file_path = config_file_paths.substring(0, comma).trim();
                    config_file_paths = config_file_paths.substring(comma + 1);
                } else {
                    config_file_path = config_file_paths.trim();
                    config_file_paths = "";
                }

                if (config_file_path.length() < 1) {
                    break;
                }
                TemplatesConfig tlconfig = parseTemplatesConfigFile(context,
                        digester, config_file_path);
                if (tlconfig != null) {
                    //  
                    patternMapping.putAll(tlconfig.getPatterns());
                    formatterMapping.putAll(tlconfig.getFormatters());
                }
            } 

        } catch (UnavailableException e) {
            e.printStackTrace();
            throw e;
        }

        // TODO: 处理继承关系
        
        
        templatesMapping = new HashMap();
        templatesMapping.put(PATTERNS_CONFIG_KEY, patternMapping);
        templatesMapping.put(FORMATERS_CONFIG_KEY, formatterMapping);

        context.setAttribute(TEMPLATES_CONFIG_APPLICATION_KEY, templatesMapping);
    }

    /**
     * 创建Digester对象.
     * 
     * @return
     */
    protected Digester createConfigDigester() {
        Digester configDigester = null;
        // Create a new Digester instance with standard capabilities
        configDigester = new Digester();
        configDigester.setNamespaceAware(true);
        configDigester.setValidating(true);
        configDigester.setUseContextClassLoader(true);
        configDigester.addRuleSet(new ConfigRuleSet());
        for (int i = 0; i < registrations.length; i += 2) {
            URL url = this.getClass().getResource(registrations[i + 1]);
            if (url != null) {
                configDigester.register(registrations[i], url.toString());
            }
        }
        // Return the completely configured Digester instance
        return configDigester;
    }

    /**
     * 根据指定的配置文件对象,重新组织, 处理配置信息中的继承关系.
     * 
     * 注意:返回值与参数是同一实例.
     * 
     * @return 组织后的配置文件对象
     * @throws UnavailableException
     */
    protected TemplatesConfig processExtends(TemplatesConfig tlconfig)
            throws UnavailableException {
        TemplatesConfig clonedTemplatesConfig = cloneTemplatesConfig(tlconfig);
        Map patterns = tlconfig.getPatterns();
        Map clonedPatterns = clonedTemplatesConfig.getPatterns();
        Collection patternsColl = patterns.values();
        Iterator patternsIterator = patternsColl.iterator();
        while (patternsIterator.hasNext()) {
            PatternConfig pconfig = (PatternConfig) patternsIterator.next();
            if (pconfig.getExtends() != null) {
                Map templates = pconfig.getTemplateConfigs();
                Map templates_e = patternMap(clonedPatterns, new Stack(),
                        pconfig);
                templates.clear();
                templates.putAll(templates_e);
            }
        }
        return tlconfig;
    }

    /**
     * 使用递归,分析继承关系, 并取得继承来的模版集合.
     * 
     * @param patterns
     * @param stack
     * @param patternConfig
     * @return
     * @throws Exception
     */
    private Map patternMap(Map patterns, Stack stack,
            PatternConfig patternConfig) throws UnavailableException {
        Map newPatternMap = new TreeMap();
        stack.push(patternConfig.getId());
        if (patternConfig.getExtends() != null) {
            if (stack.contains(patternConfig.getExtends())) {
                // 继承关系错误 TODO
                throw new UnavailableException("继承关系错误");
            }
            PatternConfig parentPatternConfig = (PatternConfig) patterns
                    .get(patternConfig.getExtends());
            if (parentPatternConfig == null) {
                // TODO
                throw new UnavailableException("没有找到定义模版."
                        + patternConfig.getExtends());
            }
            Map parentTemplates = patternMap(patterns, stack,
                    parentPatternConfig);
            newPatternMap.putAll(parentTemplates);
        }
        Map templates = patternConfig.getTemplateConfigs();
        newPatternMap.putAll(templates);
        stack.pop();
        return newPatternMap;
    }

    /**
     * 复制指定的参数配置对象.
     * 
     * @param oldParamConfig
     * @return 新的实例
     */
    private ParamConfig cloneParamConfig(ParamConfig oldParamConfig) {
        if (oldParamConfig == null) {
            return null;
        }
        ParamConfig newParamConfig = new ParamConfig();
        newParamConfig.setName(oldParamConfig.getName());
        newParamConfig.setValue(oldParamConfig.getValue());
        return newParamConfig;
    }

    /**
     * 复制指定的模版配置对象.
     * 
     * @param oldTemplateConfig
     * @return 新的实例
     */
    private TemplateConfig cloneTemplateConfig(TemplateConfig oldTemplateConfig) {
        if (oldTemplateConfig == null) {
            return null;
        }
        TemplateConfig newTemplateConfig = new TemplateConfig();
        newTemplateConfig.setId(oldTemplateConfig.getId());
        newTemplateConfig.setSrc(oldTemplateConfig.getSrc());
        newTemplateConfig.setDest(oldTemplateConfig.getDest());
        newTemplateConfig.setFormatter(oldTemplateConfig.getFormatter());
        newTemplateConfig.setOutit(oldTemplateConfig.isOutit());
        Map params = oldTemplateConfig.getParams();
        Collection paramsColl = params.values();
        Iterator paramsIterator = paramsColl.iterator();
        while (paramsIterator.hasNext()) {
            ParamConfig paramConfig1 = (ParamConfig) paramsIterator.next();
            ParamConfig paramConfig = cloneParamConfig(paramConfig1);
            newTemplateConfig.addParamConfig(paramConfig);
        }
        return newTemplateConfig;
    }

    /**
     * 复制指定的模版集合配置对象.
     * 
     * @param oldPatternConfig
     * @return 新的实例
     */
    private PatternConfig clonePatternConfig(PatternConfig oldPatternConfig) {
        if (oldPatternConfig == null) {
            return null;
        }
        PatternConfig newPatternConfig = new PatternConfig();
        newPatternConfig.setId(oldPatternConfig.getId());
        newPatternConfig.setCharset(oldPatternConfig.getCharset());
        newPatternConfig.setExtends(oldPatternConfig.getExtends());
        Map oldTemplateConfigs = oldPatternConfig.getTemplateConfigs();
        Collection templates1Coll = oldTemplateConfigs.values();
        Iterator templates1Iterator = templates1Coll.iterator();
        while (templates1Iterator.hasNext()) {
            TemplateConfig tconfig = (TemplateConfig) templates1Iterator.next();
            TemplateConfig newTconfig = cloneTemplateConfig(tconfig);
            newPatternConfig.addTemplateConfig(newTconfig);
        }
        return newPatternConfig;
    }

    /**
     * 复制指定的配置对象.
     * 
     * @param oldTemplatesConfig
     * @return 新的实例
     */
    private TemplatesConfig cloneTemplatesConfig(
            TemplatesConfig oldTemplatesConfig) {
        if (oldTemplatesConfig == null) {
            return null;
        }
        TemplatesConfig newTemplatesConfig = new TemplatesConfig();
        newTemplatesConfig.setId(oldTemplatesConfig.getId());
        Map oldPatterns = oldTemplatesConfig.getPatterns();
        Collection patternsColl = oldPatterns.values();
        Iterator patternsIterator = patternsColl.iterator();
        while (patternsIterator.hasNext()) {
            PatternConfig patternConfig = (PatternConfig) patternsIterator
                    .next();
            PatternConfig newPatternConfig = clonePatternConfig(patternConfig);
            newTemplatesConfig.addPatternConfig(newPatternConfig);
        }
        return newTemplatesConfig;
    }

    /**
     * 根据指定的配置文件,取得配置信息对象.
     * 
     * @param digester
     * @param is
     * @return
     * @throws UnavailableException
     */
    protected TemplatesConfig parseTemplatesConfigFile(ServletContext context,
            Digester digester, String path) throws UnavailableException {
        InputStream input = null;
        try {
            URL url = context.getResource(path);
            InputSource is = new InputSource(url.toExternalForm());
            input = context.getResourceAsStream(path);
            is.setByteStream(input);
            TemplatesConfig tlconfig = (TemplatesConfig) digester.parse(is);
            return tlconfig;
        } catch (IOException e) {
            throw new UnavailableException(e.getMessage());
        } catch (SAXException e) {
            throw new UnavailableException(e.getMessage());
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    throw new UnavailableException(e.getMessage());
                }
            }
        }
    }

    public boolean isLoaded(ServletContext context) {
        return context.getAttribute(TEMPLATES_CONFIG_APPLICATION_KEY)!=null;
    }
}
